using System;
using System.Collections.Generic;

namespace lab01
{
    internal interface IMatrix
    {
        void SetValue(int x, int y, int value);
        int GetValue(int x, int y);
        int GetNumRows();
        int GetNumColumns();
    }

    internal class SomeMatrix : IMatrix
    {
        protected List<IVector> MatrixColumns;

        #region IMatrix Members

        public void SetValue(int x, int y, int value)
        {
            CheckRange(x);
            MatrixColumns[x].SetValue(y, value);
        }

        public int GetValue(int x, int y)
        {
            CheckRange(x);
            return MatrixColumns[x].GetValue(y);
        }

        public int GetNumRows()
        {
            return MatrixColumns[0].GetNumRows();
        }

        public int GetNumColumns()
        {
            return MatrixColumns.Count;
        }

        #endregion

        private void CheckRange(int x)
        {
            if (!(0 <= x && x < GetNumColumns()))
                throw new IndexOutOfRangeException();
        }
    }

    internal class DenseMatrix : SomeMatrix
    {
        public DenseMatrix(int width, int height)
        {
            MatrixColumns = new List<IVector>(width);
            for (var i = 0; i < width; i++)
            {
                MatrixColumns.Add(new DenseVector(height));
            }
        }
    }

    internal class SparseMatrix : SomeMatrix
    {
        public SparseMatrix(int width, int height)
        {
            MatrixColumns = new List<IVector>(width);
            for (var i = 0; i < width; i++)
            {
                MatrixColumns.Add(new SparseVector(height));
            }
        }
    }

    internal class MatrixRoutines
    {
        public static void Fill(IMatrix matrix, int numNonZeros, int maxValue)
        {
            var rnd = new Random();
            for (var i = 0; i < numNonZeros; i++)
            {
                var value = rnd.Next(1, maxValue);
                var isAdded = false;
                while (!isAdded)
                {
                    var x = rnd.Next(0, matrix.GetNumColumns());
                    var y = rnd.Next(0, matrix.GetNumRows());
                    if (matrix.GetValue(x, y) != 0) continue;
                    matrix.SetValue(x, y, value);
                    isAdded = true;
                }
            }
        }

        public static void Print(IMatrix matrix)
        {
            for (var y = 0; y < matrix.GetNumRows(); y++)
            {
                for (var x = 0; x < matrix.GetNumColumns(); x++)
                {
                    Console.Write("{0,4}", matrix.GetValue(x, y));
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }

    internal class MatrixStatistics
    {
        private readonly IMatrix _matrix;

        public MatrixStatistics(IMatrix m)
        {
            _matrix = m;
        }

        public int Sum()
        {
            var sum = 0;
            for (var x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (var y = 0; y < _matrix.GetNumRows(); y++)
                {
                    sum += _matrix.GetValue(x, y);
                }
            }
            return sum;
        }

        public int Nonz()
        {
            var nonz = 0;
            for (var x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (var y = 0; y < _matrix.GetNumRows(); y++)
                {
                    if (_matrix.GetValue(x, y) != 0) nonz++;
                }
            }
            return nonz;
        }

        public int Max()
        {
            var max = 0;
            for (var x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (var y = 0; y < _matrix.GetNumRows(); y++)
                {
                    var cur = _matrix.GetValue(x, y);
                    if (cur > max) max = cur;
                }
            }
            return max;
        }

        public double Middle()
        {
            return 1.0*Sum()/Nonz();
        }
    }
}